#include "profile_tracer.h"
#include <iomanip>
#include <sstream>
#include <glog/logging.h>

namespace csugl::pt
{
    ///////////////////////////
	/// InstrumentContainer
	///////////////////////////

	void InstrumentCache::WriteFileContent()
	{
		auto start = std::chrono::high_resolution_clock::now();
		std::stringstream json;

		for(int tmpIndex = 0; tmpIndex < m_Q.rear()->size(); tmpIndex++)
		{
			auto &result = (*m_Q.rear())[tmpIndex];
			
			json << std::setprecision(3) << std::fixed;
			json << ",\n{\"cat\":\"function\",\"dur\":";
			json << result.elapsedTime.count();
			json << ",\"name\":\"";
			json << result.name;
			json << "\",\"ph\":\"X\",\"pid\":0,\"tid\":";
			json << result.threadID;
			json << ",\"ts\":";
			json << result.start.count();
			json << "}";
		}
		m_OutputStream << json.str();
		m_OutputStream.flush();

		LOG(INFO) << "saved, size: " << m_Q.size() << ", used " << (FloatingPointMicroSeconds(std::chrono::high_resolution_clock::now().time_since_epoch()) - 
		FloatingPointMicroSeconds(start.time_since_epoch())).count() * 0.001 << "ms";
		
		delete m_Q.rear();
		m_Q.pop_back();
	}

	bool InstrumentCache::Begin(const std::string filePath)
	{
		m_OutputStream.open(filePath);
		auto fos_open = m_OutputStream.is_open();
		if (fos_open) 
		{
			auto r_array = new std::vector<ProfileResult>();
			r_array->reserve(maxSize);
			m_Q.push_front(r_array);
			is_writting_content = true;
			m_Save_thread = std::thread{[this]()
			{
				bool begin_ending = false;
				while (true) 
				{
					if (m_Q.size() > 1)
					{
						WriteFileContent();
						
						if (!begin_ending && !is_writting_content)
						{
							begin_ending = true;
							LOG(WARNING) << "Profile File is Saving, Please Don't Exit";
						}
					}
					else if (is_writting_content == false)
					{
						WriteFileContent();
						break;
					}
				}
				m_OutputStream << "]}";
				m_OutputStream.flush();
			}};
		}
		return fos_open;
	}

	void InstrumentCache::End()
	{
		m_Save_thread.join();
		m_OutputStream.close();
	}

	void InstrumentCache::operator<<(const ProfileResult& result)
	{
		m_Q.front()->push_back(result);
		curResult++;
		if (curResult == maxSize)
		{
			auto r_array = new std::vector<ProfileResult>();
			r_array->reserve(maxSize);
			m_Q.push_front(r_array);
			curResult = 0;
		}
	}

	void InstrumentCache::WriteFileHeader()
	{
		m_OutputStream << "{\"otherData\": {},\"traceEvents\":[{}";
		m_OutputStream.flush();
	}

	void InstrumentCache::WriteFileFooter()
	{
		is_writting_content = false;
	}

	///////////////////////////
	/// Instrumentor
	///////////////////////////

	void Instrumentor::BeginSession(const std::string& name, const std::string& filePath)
	{
		std::lock_guard lock(m_Mutex);
		if (m_CurrentSession)
		{
			// if (Log::GetCoreLogger())
			// {
			// 	Log::ErrorCore("Instrument::BeginSession('{0}') when session '{1}' already open.",
			// 		name, m_CurrentSession->name);
			// }
            LOG(ERROR) << "Instrument::BeginSession('" << name << "') when session '" << m_CurrentSession->name << "' already open.";
            InternalEndSession();
		}
        if (InstrumentCache::GetInstance().Begin(filePath))
		// if (singleton<InstrumentCache>::getInstance()->Begin(filePath))
		{
			m_CurrentSession = new InstrumentationSession({ name });
			WriteHeader();
		}
		else
		{
			// if (Log::GetCoreLogger())
			// {
			// 	Log::ErrorCore("Instrument could not open results file '{0}'.", filePath);
            // }
            LOG(ERROR) << "Instrument could not open results file '" << filePath << "'.";
		}
	}

	void Instrumentor::EndSession()
	{
		std::lock_guard lock(m_Mutex);
		InternalEndSession();
	}

	void Instrumentor::WriteProfile(ProfileResult&& result)
	{
		{
			std::lock_guard lock(m_Mutex);
			if (m_CurrentSession)
			{
				InstrumentCache::GetInstance() << result;
                // (*singleton<InstrumentCache>::getInstance()) << result;
			}
		}

	}

	void Instrumentor::InternalEndSession()
	{
		if (m_CurrentSession)
		{
			WriteFooter();

			InstrumentCache::GetInstance().End();
            // singleton<InstrumentCache>::getInstance()->End();

			delete m_CurrentSession;
			m_CurrentSession = nullptr;
		}
	}
} // namespace csugl::pt
